/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.jtsmix.mixfile;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

/**
 * Decoder for Blowfish key of encrypted mix files.
 *
 * @author halecivo
 * @author Damien Carol
 */

public class WestwoodKey {

    private static void OUT_TAB(String name, int size, byte[] tab) {
        System.out.println(">>>>>>>>>>>>");
        System.out.println(name);
        for (int i = 0; i < size; i++)
            System.out.print("0x" + String.format("%02X", tab[i]) + ", ");
        System.out.println("\n>>>>>>>>>>>>");

    }


    private static void OUT_TAB_64(String name, int size, long[] tab) {
//        System.out.println(">>>>>>>>>>>>");
//        System.out.println(name);
//        for (int i = 0; i < size; i++)
//            System.out.print("0x" + String.format("%08X", (int) tab[i]) + ", ");
//        System.out.println("\n>>>>>>>>>>>>");

    }


    private static void OUT_TAB_16(String name, int len, int[] tab) {
        OUT_TAB_16(name, len, tab, 0);
    }

    private static void OUT_TAB_16(String name, int len, int[] tab,
                                   int tab_ptr) {

//        System.out.println(">>>>>>>>>>>>");
//        System.out.println(name);
//        for (int i = tab_ptr; i < tab_ptr + len; i++)
//            System.out.print("0x" + String.format("%04X", tab[i]) + ", ");
//        System.out.println("\n>>>>>>>>>>>>");
    }

    private static byte[] pubkey_str = "AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V".getBytes();

    private static byte char2num[] = new byte[]{
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
            -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    };

	/*typedef Uint32 bignum4[4];
    typedef Uint32 bignum[64];
	typedef Uint32 bignum130[130];

	struct pubkey_t {
	    bignum key1;
	    bignum key2;
	    Uint32 len;
	} pubkey;
	bignum glob1;
	Uint32 glob1_bitlen, glob1_len_x2;
	bignum130 glob2;
	bignum4 glob1_hi, glob1_hi_inv;
	Uint32 glob1_hi_bitlen;
	Uint32 glob1_hi_inv_lo, glob1_hi_inv_hi;*/

    private static pubkey_t pubkey;
    static long[] glob1 = new long[64];
    static long glob1_bitlen;

    static long glob1_len_x2;
    static long[] glob2 = new long[130];
    static long[] glob1_hi = new long[4];
    static long[] glob1_hi_inv = new long[4];
    static long glob1_hi_bitlen;
    static int glob1_hi_inv_lo, glob1_hi_inv_hi;

    public static byte[] get_blowfish_key(byte[] WSKey_in) throws Exception {

        pubkey = new pubkey_t();

        byte[] keyTmp = new byte[256];

        init_pubkey();

        long len_pred_res = len_predata();

        process_predata(WSKey_in, len_pred_res, keyTmp);

        byte[] key = Arrays.copyOf(keyTmp, 56);
        return key;
    }

    // void process_predata(const Uint8* pre, Uint32 pre_len, Uint8 *buf)
    private static void process_predata(byte[] pre, long len_pred_res, byte[] buf) throws Exception {
        long[] n2 = new long[64];
        long[] n3 = new long[64];
        int buf_ptr = 0;
        int pre_ptr = 0;


        int a = (int) ((pubkey.len - 1) / 8);

        while (a + 1 <= len_pred_res) {
            //System.out.println("a + 1 = " + (a + 1));
            //System.out.println("pre_len = " +  pre_len);

            init_bignum(n2, 0, 64);

            //memmove(n2, pre, a + 1); // with offset pre_ptr
            //OUT_TAB_64("n2 before move", 64, n2);
            ByteBuffer bf = ByteBuffer.wrap(pre);
            bf.order(ByteOrder.LITTLE_ENDIAN);
            bf.position(pre_ptr);
            for (int jj = 0; jj < ((a + 1) / 4); jj++)
                n2[jj] = bf.getInt();
            //OUT_TAB_64("n2 after move", 64, n2);


            calc_a_key(n3, n2, pubkey.key2, pubkey.key1, 64);


            ByteBuffer bf_out = ByteBuffer.wrap(buf);
            bf_out.order(ByteOrder.LITTLE_ENDIAN);
            bf_out.position(buf_ptr);
            for (int jj = 0; jj < ((a + 1) / 4); jj++)
                bf_out.putInt((int) n3[jj]);
            //  memmove(buf, n3, a);


            len_pred_res -= a + 1;
            pre_ptr += a + 1;
            buf_ptr += a;
        }
    }

    // void calc_a_key(bignum n1, bignum n2, bignum n3, bignum n4, Uint32 len)
    private static void calc_a_key(long[] n1, long[] n2, long[] n3,
                                   long[] n4, int len) throws Exception {

        long[] n_tmp = new long[64];
        int n3_len, bit_mask;
        long n4_len;
        long n3_bitlen;
        int n3_ptr = 0;

        init_bignum(n1, 1, len);
        n4_len = len_bignum(n4, len);
        //System.out.println("n4_len = " + n4_len);
        init_two_dw(n4, n4_len);
        OUT_TAB_64("n4", 64, n4);

        n3_bitlen = bitlen_bignum(n3, n4_len);
        n3_len = (int) ((n3_bitlen + 31) / 32);
        //System.out.println("n3_bitlen = " + n3_bitlen);
        //System.out.println("n3_len = " + n3_len);
        bit_mask = ((1) << ((n3_bitlen - 1) % 32)) >> 1;
        n3_ptr += n3_len - 1;
        n3_bitlen--;
        mov_bignum(n1, 0, n2, 0, n4_len); //int a=5;
        while ((long) --n3_bitlen != -1) {
            if (bit_mask == 0) {
                bit_mask = 0x80000000;
                n3_ptr--;
            }
            calc_a_bignum(n_tmp, n1, n1, n4_len);

            if ((n3[n3_ptr] & bit_mask) != 0) {
                calc_a_bignum(n1, n_tmp, n2, n4_len);
            } else {
                mov_bignum(n1, 0, n_tmp, 0, n4_len);

            }
            bit_mask >>>= 1;

            //a--;
            //if (a <0)
            //   throw new Exception();
        }
        //if (true)
        //throw new Exception();

        init_bignum(n_tmp, 0, n4_len);
        clear_tmp_vars(len);
    }

    //void calc_a_bignum(bignum n1, bignum n2, bignum n3, Uint32 len)
    private static void calc_a_bignum(long[] n1, long[] n2, long[] n3, long len) throws Exception {
        long g2_len_x2;
        long len_diff;
        long esi_ptr;
        long edi_ptr;
        int glb2_ptr = 0; // These are pointer to bignum glob2 but int16
        int[] glb2 = new int[128];
        int tmp;

//        System.out.println(">>>>>>>>>>>>");
//        System.out.println("CALC BIGNUM");
//        System.out.println(">>>>>>>>>>>>");
//
//        System.out.println(">>>>>>>>>>>>");
//        System.out.println("len 0x" + String.format("%08X", len) + "");
//        System.out.println(">>>>>>>>>>>>");

        OUT_TAB_64("glob2 before mul_bignum1", (int) len, glob2);
        mul_bignum1(glob2, 0, n2, 0, n3, 0, (int) len);
        OUT_TAB_64("glob2 after mul_bignum1", (int) len, glob2);

        glob2[(int) (len * 2)] = 0;
        g2_len_x2 = len_bignum(glob2, len * 2 + 1) * 2;

        if (g2_len_x2 >= glob1_len_x2) {
            inc_bignum(glob2, (int) (len * 2 + 1));
            neg_bignum(glob2, (int) (len * 2 + 1));
            len_diff = g2_len_x2 + 1 - glob1_len_x2;
            OUT_TAB_64("glob2 after inc_bignum / neg_bignum", 64, glob2);

            //#if SDL_BYTEORDER == SDL_BIG_ENDIAN
            glb2 = endian_Uint32ToUint16ArracyCast(glob2, 64);
            //#else
            //glb2 = (Uint16*) glob2;
            //#endif

            esi_ptr = glb2_ptr + (1 + g2_len_x2 - glob1_len_x2);
            edi_ptr = glb2_ptr + (g2_len_x2 + 1);

            for (; len_diff != 0; len_diff--) {
                edi_ptr--;

                OUT_TAB_16("glb2", 64, glb2);
                    /*System.out.println(">>>>>>>>>>>>");
                    System.out.println("edi");
		    		for (int i=0; i< 64; i++)
		    			System.out.print("0x" + String.format("%04X", glb2[edi_ptr-i]) + ", ");
		    		System.out.println("\n>>>>>>>>>>>>");*/

                tmp = (int) get_mulword(glb2, (int) edi_ptr);
//                System.out.println(">>>>>>>>>>>>");
//                System.out.println("tmp 0x" + String.format("%04X", tmp) + "");
//                System.out.println(">>>>>>>>>>>>");


                esi_ptr--;
                if (tmp > 0) {
                    mul_bignum_word(glb2, (int) esi_ptr, glob1, 0, tmp, (int) (2 * len));

                    //if ((glb2[(int)edi_ptr] & 0x8000) == 0) {
                    if ((glb2[(int) edi_ptr] & 0x8000) == 0) {
                        System.out.println(">>>>>>>>>>>>");
                        System.out.println("& 0x8000");
                        System.out.println(">>>>>>>>>>>>");


                        if (sub_bignum1(glb2, (int) esi_ptr, glb2, (int) esi_ptr, glob1, 0, len) > 0) {
                            glb2[(int) edi_ptr]--;
                        }
                    }
                }
            }
            //if (true)
            //	throw new Exception();


            // recopie
            for (int a = 0; a < len; a++) {
                glob2[a] = glb2[a * 2] + ((glb2[(a * 2) + 1] << 16) & 0xFFFF0000);
            }


            OUT_TAB_64("glob2 before neg_bignum / dec_bignum", (int) len, glob2);

            neg_bignum(glob2, (int) len);
            OUT_TAB_64("glob2 after neg_bignum", (int) len, glob2);
            dec_bignum(glob2, 0, (int) len);

            OUT_TAB_64("glob2 after neg_bignum / dec_bignum", (int) len, glob2);

            // FIXME Check swap in calc_a_bignum
            //#if SDL_BYTEORDER == SDL_BIG_ENDIAN
            //for (int j = 0; j < 64; j++){
            //	glob2[j] = SDL_Swap32(glob2[j]);
            //}
            //Uint16 *tmp = (Uint16*)glob2;
            //for (int j = 0; j < 64; j++){
            //	tmp[j] = SDL_Swap16(tmp[j]);
            //}
            //#endif
        }
		    
		/*OUT_TAB_64("n1 before mov_bignum", (int) len, n1);*/
        mov_bignum(n1, 0, glob2, 0, len);
        OUT_TAB_64("n1 after mov_bignum", (int) len, n1);

        //throw new Exception();
    }

    private static int[] endian_Uint32ToUint16ArracyCast(long[] glob22, int len) {
        int[] res = new int[len * 2];

        for (int i = 0; i < len; i++) {
            res[i * 2] = (int) (((glob22[i] & 0x0000FFFF)) & 0xFFFF);
            res[(i * 2) + 1] = (int) (((glob22[i] & 0xFFFF0000) >> 16) & 0xFFFF);
        }

        return res;
    }


    //Uint32 sub_bignum1(Uint16 *dest, Uint16* src1, bignum src2, Uint32 carry, Sint64 len)
    private static int sub_bignum1(int[] dest, int dest_ptr, int[] src1, int src1_ptr, long[] src2, int carry,
                                   long len) throws Exception {

        int src2_ptr = 0;
        int[] src2_short = new int[(int) (len * 2)];
        int src2_short_ptr = 0;

        for (int a = 0; a < len; a++) {
            src2_short[a * 2] = (int) (src2[src2_ptr + a] & 0xFFFF);
            src2_short[(a * 2) + 1] = (int) ((src2[src2_ptr + a] >> 16) & 0xFFFF);
        }

        while ((long) --len != -1) {
            dest[dest_ptr] = (int) ((src1[src1_ptr] - src2_short[src2_short_ptr] - carry) & 0xFFFF);

            if (((src1[src1_ptr] - src2_short[src2_short_ptr] - carry) & 0x10000) != 0)
                carry = 1;
            else
                carry = 0;

            dest_ptr++;
            src1_ptr++;
            src2_ptr++;
        }

        return carry;
    }

    private static long get_mulword(int[] wn, int wn_ptr) throws Exception {

        long i = 0;
        int wn_0 = wn[wn_ptr];
        int wn_1 = wn[wn_ptr - 1];
        int wn_2 = wn[wn_ptr - 2];
		

		/*System.out.println(">>>>>>>>>>>>");
		System.out.println("wn_0 0x" + String.format("%04X", wn_0));
		System.out.println("wn_1 0x" + String.format("%04X", wn_1));
		System.out.println("wn_2 0x" + String.format("%04X", wn_2));
		System.out.println("glob1_hi_inv_lo 0x" + String.format("%08X", glob1_hi_inv_lo));
		System.out.println(">>>>>>>>>>>>");*/

        i = ((wn_1 ^ 0xffff) & 0xffff) * glob1_hi_inv_lo;
        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(String.format("i 0x%08X", i));
        i = i + 65536; // 0x1.0000
        i = i >> 1;
        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(String.format("i 0x%08X", i));
        i = i + (((wn_2 ^ 0xffff) * glob1_hi_inv_hi + glob1_hi_inv_hi) >>> 1);
        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(String.format("i 0x%08X", i));
        i = i + 1;
        i = i >>> 16;
        i = i + ((((wn_1 ^ 0xffff) & 0xffff) * glob1_hi_inv_hi) >>> 1);
        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(String.format("i 0x%08X", i));
        i = i + (((wn_0 ^ 0xffff) * glob1_hi_inv_lo) >>> 1);
        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(String.format("i 0x%08X", i));
        i = i + 1;
        i = i >>> 14;
        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(String.format("i 0x%08X", i));
        i = i + glob1_hi_inv_hi * (wn_0 ^ 0xffff) * 2;
        i = i >>> glob1_hi_bitlen;

		/*System.out.println(">>>>>>>>>>>>");
		System.out.println("i 0x" + String.format("%08X", i));
		System.out.println(">>>>>>>>>>>>");*/

        if (i > 0xffff)
            i = 0xffff;

        //throw new Exception();
        return i & 0xffff;
    }

    /**
     * @param n1_short
     * @param n1_short_ptr
     * @param n2
     * @param n2_ptr
     * @param mul_param
     * @param len
     * @throws Exception
     */
    private static void mul_bignum_word(int[] n1_short, int n1_short_ptr, long[] n2, int n2_ptr, int mul_param, int len) throws Exception {
        int mul = mul_param & 0xFFFF;

        //System.out.println(">>>>>>>>>>>>");
        //System.out.println(" mul " + String.format("%08X", mul));
        //System.out.println(">>>>>>>>>>>>");


        int[] n2_short = new int[len];
        int n2_short_ptr = 0;

        for (int a = 0; a < (len / 2); a++) {
            n2_short[a * 2] = (int) (n2[n2_ptr + a] & 0xFFFF);
            n2_short[(a * 2) + 1] = (int) ((n2[n2_ptr + a] >> 16) & 0xFFFF);
        }
        //OUT_TAB_16("N1", len, n1_short);
        //OUT_TAB_16("N2", len, n2_short);

        //Uint16 *N2;

        // FIXME Check if swap is required in mul_bignum_word
	/*#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	    bignum N22;

		for (Uint32 i = 0; i < len/2; i++){
			N22[i] = SDL_Swap32(n2[i]);
		}

		N2 = (Uint16 *)N22;

		for (Uint32 i = 0; i < len; i++){
			N2[i] = SDL_Swap16(N2[i]);
		}
	#else
		N2 = (Uint16 *)n2;
	#endif*/

        int tmp = 0;
        for (int i = 0; i < len; i++) {

            //System.out.println(">>>>>>>>>>>>");
            //System.out.println(" i " + i);
            //System.out.println(" n1[0] " + String.format("%04X", n1_short[i]));
            //System.out.println(" n2[0] " + String.format("%04X", n2_short[i]));
            //System.out.println(" tmp " + String.format("%08X", tmp));
            //System.out.println(">>>>>>>>>>>>");


            tmp += (mul * n2_short[i]);
            //System.out.println(">>>>>>>>>>>>");
            //System.out.println(" tmp " + String.format("%08X", tmp));
            //System.out.println(">>>>>>>>>>>>");

            tmp += n1_short[i + n1_short_ptr];// + tmp;
            //System.out.println(">>>>>>>>>>>>");
            //System.out.println(" tmp " + String.format("%08X", tmp));
            //System.out.println(">>>>>>>>>>>>");

            n1_short[i + n1_short_ptr] = (int) (tmp & 0xFFFF);
            //System.out.println(">>>>>>>>>>>>");
            //System.out.println(" n1[0] " + String.format("%04X", n1_short[i]));
            //System.out.println(" tmp " + String.format("%08X", tmp));
            //System.out.println(">>>>>>>>>>>>");

            //OUT_TAB_64("n1  for(" + i + ")", 64, n1);

            tmp = (tmp >> 16) & 0xFFFF;

            //System.out.println(">>>>>>>>>>>>");
            //System.out.println(" tmp " + String.format("%08X", tmp));
            //System.out.println(">>>>>>>>>>>>");
        }
        // FIXME n1_short en plus
        n1_short[len + n1_short_ptr] += tmp;

        //OUT_TAB_64("n1  for(x)", 6, n1);
        //OUT_TAB_16("N1", len, n1_short);
        //OUT_TAB_16("N2", len, n2_short);


        //throw new Exception();
	    /*for (int a=0; a<(len/2); a++)
		{
			//n1_short[a*2] = (int) (n1[n1_ptr + a] & 0xFFFF);
			//n1_short[(a*2)+1] = (int) ((n1[n1_ptr + a]>>16) & 0xFFFF);
	
			n1[n1_ptr + a] = (n1_short[a*2] + (n1_short[(a*2)+1] << 16)) & 0xFFFFFFFF;
			//n2_short[a*2] = (int) (n2[n2_ptr + a] & 0xFFFF);
			//n2_short[(a*2)+1] =  (int) ((n2[n2_ptr + a]>>16) & 0xFFFF);
		}*/

        //OUT_TAB_16("n1 dest", len, n1_short);
    }


    private static void dec_bignum(long[] n, int n_ptr, int len) throws Exception {

        //void dec_bignum(bignum n, Uint32 len)
        //{
        //    while ((--*n == 0xffffffff) && (--len > 0))
        //        n++;
        //}
		/*for (int i = 0; i < len; i++)
		{
			n[n_ptr + i]--;
		}*/
        while ((--n[n_ptr] == 0xFFFFFFFF) && (--len > 0))
            n_ptr++;
    }

    //void neg_bignum(bignum n, Uint32 len)
    private static void neg_bignum(long[] n, int len) throws Exception {

        not_bignum(n, len);
        inc_bignum(n, len);
    }

    private static void not_bignum(long[] n, int len) {

        //OUT_TAB_64("not_bignum n BEFORE", len, n);

        int i;
        for (i = 0; i < len; i++) {
            n[i] = ~(n[i]);
        }

        //OUT_TAB_64("not_bignum n AFTER", len, n);
    }

    //void mul_bignum1(bignum dest, bignum src1, bignum src2, Uint32 len)
    private static void mul_bignum1(long[] dest, int dest_ptr, long[] src1, int src1_ptr, long[] src2xxxx, int src2_ptrxxxx, int len) throws Exception {
        //throw new Exception();

        int[] n1_short = new int[len * 4];
        int n1_short_ptr = 0;
        int[] src2_short = new int[len * 4];
        int src2_short_ptr = 0;
        for (int a = 0; a < (len); a++) {
            //n1_short[a*2] = (int) (dest[dest_ptr + a] & 0xFFFF);
            //n1_short[(a*2)+1] = (int) ((dest[dest_ptr + a]>>16) & 0xFFFF);

            src2_short[a * 2] = (int) (src2xxxx[src2_ptrxxxx + a] & 0xFFFF);
            src2_short[(a * 2) + 1] = (int) ((src2xxxx[src2_ptrxxxx + a] >> 16) & 0xFFFF);

        }


        int SRC2_ptr = 0, DEST_ptr = 0;

        init_bignum(dest, 0, len * 2);

        // FIXME : Check if swap is required
        //#if SDL_BYTEORDER == SDL_BIG_ENDIAN
        //bignum SRC22, DEST3;
//
        //for (int i = 0; i < len*2; i++){
        //	SRC22[i] = SDL_Swap32(src2[i]);
        //	DEST3[i] = SDL_Swap32(dest[i]);
        //}
//
        //SRC2 = (Uint16 *)SRC22;
        //DEST = (Uint16 *)DEST3;
//
        //for (int i = 0; i < len*2; i++){
        //	SRC2[i] = SDL_Swap16(SRC2[i]);
        //}
        //#else
        //	SRC2 = (Uint16 *)src2;
        //	DEST = (Uint16 *)dest;
        //#endif

        //OUT_TAB_64("SRC2 before", 64, src2);

        for (int i = 0; i < len * 2; i++) {
            OUT_TAB_16("DEST", len * 2, n1_short, n1_short_ptr);
            System.out.println(">>>>>>>>>>>>");
            System.out.println("SRC2[0] 0x" + String.format("%04X", src2_short[SRC2_ptr]) + "\r\n");
            System.out.println(">>>>>>>>>>>>");

            mul_bignum_word(n1_short, n1_short_ptr, src1, src1_ptr, src2_short[SRC2_ptr], len * 2);

            OUT_TAB_16("DEST", len * 2, n1_short, n1_short_ptr);

            SRC2_ptr++;
            n1_short_ptr++;
        }


        for (int a = 0; a < (len * 2); a++) {
            dest[a] = n1_short[a * 2] + ((n1_short[(a * 2) + 1] << 16) & 0xFFFF0000);
        }

        //OUT_TAB_64("dest after ", 64, dest);
        //throw new Exception();

        // FIXME : Check if swap is required
        //#if SDL_BYTEORDER == SDL_BIG_ENDIAN
        //DEST = (Uint16 *)DEST3;
//
        //	for (Uint32 i = 0; i < len*4; i++){
        //		DEST[i] = SDL_Swap16(DEST[i]);
        //	}
//
        //    for (int i = 0; i < len*2; i++) {
        //		dest[i] = SDL_Swap32(DEST3[i]);
        //	}
        //#endif


    }


    //void init_two_dw(bignum n, Uint32 len)
    private static void init_two_dw(long[] n, long n4_len) throws Exception {

        mov_bignum(glob1, 0, n, 0, n4_len);
        glob1_bitlen = bitlen_bignum(glob1, n4_len);
        glob1_len_x2 = (glob1_bitlen + 15) / 16;
//        System.out.println(">>>>>>>>>>>>");
//        System.out.println("glob1_len_x2 0x" + String.format("%08X", glob1_len_x2));
//        System.out.println(">>>>>>>>>>>>");
        mov_bignum(glob1_hi, 0, glob1, (int) (len_bignum(glob1, n4_len) - 2), 2);
        glob1_hi_bitlen = bitlen_bignum(glob1_hi, 2) - 32;
//        System.out.println(">>>>>>>>>>>>");
//        System.out.println("glob1_hi_bitlen 0x" + String.format("%08X", glob1_hi_bitlen));
//        System.out.println(">>>>>>>>>>>>");

        OUT_TAB_64("glob1_hi before shr", 4, glob1_hi);
        shr_bignum(glob1_hi, glob1_hi_bitlen, 2);
        OUT_TAB_64("glob1_hi after shr", 4, glob1_hi);


        OUT_TAB_64("glob1_hi_inv before inv", 4, glob1_hi_inv);
        inv_bignum(glob1_hi_inv, glob1_hi, 2);
        OUT_TAB_64("glob1_hi_inv after inv", 4, glob1_hi_inv);


        shr_bignum(glob1_hi_inv, 1, 2);
        OUT_TAB_64("glob1_hi_inv after shr", 4, glob1_hi_inv);


        glob1_hi_bitlen = (glob1_hi_bitlen + 15) % 16 + 1;
        inc_bignum(glob1_hi_inv, 2);

        OUT_TAB_64("glob1_hi_inv after inc", 4, glob1_hi_inv);


        if (bitlen_bignum(glob1_hi_inv, 2) > 32) {
            shr_bignum(glob1_hi_inv, 1, 2);
            glob1_hi_bitlen--;
        }

        glob1_hi_inv_lo = (int) ((glob1_hi_inv[0]) & 0xFFFF);
        glob1_hi_inv_hi = (int) ((glob1_hi_inv[0] >> 16) & 0xFFFF);


//        System.out.println(">>>>>>>>>>>>");
//        System.out.println("glob1_hi_inv_lo 0x" + String.format("%08X", glob1_hi_inv_lo));
//        System.out.println("glob1_hi_inv_hi 0x" + String.format("%08X", glob1_hi_inv_hi));
//        System.out.println(">>>>>>>>>>>>");


        //throw new Exception();
    }


    //void inv_bignum(bignum n1, bignum n2, Uint32 len)
    private static void inv_bignum(long[] n1, long[] n2, int len) throws Exception {
        //throw new Exception();

        long[] n_tmp = new long[64];
        int n2_bytelen;
        long bit;
        long n2_bitlen;
        int n1_ptr = 0;


        init_bignum(n_tmp, 0, len);
        init_bignum(n1, 0, len);
        n2_bitlen = bitlen_bignum(n2, len);
        bit = (((int) 1) << (n2_bitlen % 32)) & 0xFFFFFFFF;

        n1_ptr += ((n2_bitlen + 32) / 32) - 1;

        n2_bytelen = (int) (((n2_bitlen - 1) / 32) * 4);


        long tmp_n2_bitlen;

        tmp_n2_bitlen = (((n2_bitlen - 1) & 0x1f) & 0xFFFFFFFF);

        tmp_n2_bitlen = ((((long) 1) << tmp_n2_bitlen) & 0xFFFFFFFF);

        n_tmp[n2_bytelen / 4] = (((long) 0) | tmp_n2_bitlen) & 0xFFFFFFFF;


        while (n2_bitlen > 0) {
            n2_bitlen--;

            //OUT_TAB_64("n_tmp before shl", len, n_tmp);
            shl_bignum(n_tmp, 1, len);
            //OUT_TAB_64("n_tmp after shl", len, n_tmp);

            if (cmp_bignum(n_tmp, n2, len) != -1) {
                sub_bignum(n_tmp, n_tmp, n2, 0, len);
                n1[n1_ptr] |= bit;
                //System.out.println(">>>>>>>>>>>>");
                //System.out.println("OR");
            }
            bit >>>= 1;
            if (bit == 0) {
                n1_ptr--;
                bit = 2147483648l;
            }
	        
	        /*System.out.println(">>>>>>>>>>>>");
	        System.out.println("n2_bitlen 0x" + String.format("%08X", n2_bitlen));
	        System.out.println("bit 0x" + String.format("%08X", bit));
			System.out.println(">>>>>>>>>>>>");
			OUT_TAB_64("n1 ", len, n1);*/
        }
        init_bignum(n_tmp, 0, len);
    }

    private static long sub_bignum(long[] dest, long[] src1, long[] src2,
                                   long carry, int len) throws Exception {
        // Uint32 sub_bignum(bignum dest, bignum src1, bignum src2, Uint32 carry, Sint64 len)
        int src1_ptr = 0;
        int[] src1_short = new int[len * 2];
        int src1_short_ptr = 0;

        for (int a = 0; a < len; a++) {
            src1_short[a * 2] = (int) (src1[src1_ptr + a] & 0xFFFF);
            src1_short[(a * 2) + 1] = (int) ((src1[src1_ptr + a] >> 16) & 0xFFFF);
        }

        int src2_ptr = 0;
        int[] src2_short = new int[len * 2];
        int src2_short_ptr = 0;

        for (int a = 0; a < len; a++) {
            src2_short[a * 2] = (int) (src2[src2_ptr + a] & 0xFFFF);
            src2_short[(a * 2) + 1] = (int) ((src2[src2_ptr + a] >> 16) & 0xFFFF);
        }

        int dest_short_ptr = 0;
        int[] dest_short = new int[len * 2];

        int len_loop = len * 2;

        while (--len_loop != -1) {
            dest_short[dest_short_ptr] = (int) ((src1_short[src1_short_ptr] - src2_short[src2_short_ptr] - carry) & 0xFFFF);

            if (((src1_short[src1_short_ptr] - src2_short[src2_short_ptr] - carry) & 0x10000) != 0)
                carry = 1;
            else
                carry = 0;

            dest_short_ptr++;
            src1_short_ptr++;
            src2_short_ptr++;
        }

        for (int a = 0; a < len; a++) {
            dest[a] = dest_short[a * 2] + ((dest_short[(a * 2) + 1] << 16) & 0xFFFF0000);
        }

        return carry;
    }

    private static long cmp_bignum(long[] n1, long[] n2, int len) throws Exception {
        //long int cmp_bignum(bignum n1, bignum n2, Uint32 len)
		/*n1 += len-1;
		n2 += len-1;
		while (len > 0) {
		    if (*n1 < *n2)
		        return -1;
		    if (*n1 > *n2)
		        return 1;
		    n1--;
		    n2--;
		    len--;
		}
		return 0;*/
		/*System.out.println(">>>>>>>>>>>>");
        System.out.println("CMP");
		System.out.println(">>>>>>>>>>>>");
		
		
		OUT_TAB_64("n1", len, n1);
		OUT_TAB_64("n2", len, n2);

        System.out.println(">>>>>>>>>>>>");
        System.out.println("len = 0x" + String.format("%08X", len) + " ");
		System.out.println(">>>>>>>>>>>>");*/

        while (len > 0) {

	        /*System.out.println(">>>>>>>>>>>>");
	        System.out.print("*n1 = 0x" + String.format("%08X", n1[len-1]) + " ");
	        System.out.println("*n2 = 0x" + String.format("%08X", n2[len-1]) + " ");
			System.out.println(">>>>>>>>>>>>");*/

            if (n1[len - 1] < n2[len - 1])
                return -1;
            if (n1[len - 1] > n2[len - 1])
                return 1;
            len--;
        }
        return 0;
    }

    private static void shl_bignum(long[] n, int bits, int len) throws Exception {
        //void shl_bignum(bignum n, Uint32 bits, Uint32 len)

        int i, i2;

        i2 = bits / 32;
        if (i2 > 0) {
            for (i = len - 1; i > i2; i--)
                n[i] = n[i - i2];
            for (; i > 0; i--)
                n[i] = 0;
            bits = bits % 32;
        }

        //OUT_TAB_64("n middle", len, n);

        if (bits == 0)
            return;
        for (i = len - 1; i > 0; i--) {
            long tmp, tmp2;
            tmp = n[i] << bits;
            tmp2 = (((int) n[i - 1]) >>> (32 - bits)) & 0xFFFFFFFF;
            n[i] = tmp | tmp2;
	        
	        /*System.out.println(">>>>>>>>>>>>");
	        System.out.print("tmp = 0x" + String.format("%08X", tmp) + " ");
	        System.out.print("tmp2 = 0x" + String.format("%08X", tmp2) + " ");
	        System.out.println("ni = 0x" + String.format("%08X", n[i]) + " ");
			System.out.println(">>>>>>>>>>>>");*/

        }
        n[0] <<= bits;
    }

    // void shr_bignum(bignum n, Uint32 bits, long int len)
    private static void shr_bignum(long[] n, long bits, int len) {
        int i;
        long i2;

        i2 = bits / 32;
        if (i2 > 0) {
            for (i = 0; i < len - i2; i++)
                n[i] = n[(int) (i + i2)];
            for (; i < len; i++)
                n[i] = 0;
            bits = bits % 32;
        }
        if (bits == 0)
            return;
        for (i = 0; i < len - 1; i++)
            n[i] = (n[i] >> bits) | (n[i + 1] << (32 - bits));
        n[i] = n[i] >> bits;
    }

    private static void mov_bignum(long[] dest, int dest_ptr, long[] src, int src_ptr, long len) throws Exception {

        System.out.println(">>>>>>>>>>>>");
        System.out.println("MOV BIGNUM");
        System.out.println(">>>>>>>>>>>>");

        //mov_bignum(dest, src, n4_len);
        //}

        //void mov_bignum(bignum dest, bignum src, Uint32 len)
        //private static void mov_bignum(long[] dest, long[] src, int len, long l) throws Exception {
        // original : memmove(dest, src, len*4);


        //if ((len%4) != 0)
        //	throw new Exception();

        for (int i = 0; i < len; i++)
            dest[dest_ptr + i] = src[src_ptr + i];
    }

    //void inc_bignum(bignum n, Uint32 len)
    private static void inc_bignum(long[] n, int len) {
        inc_bignum(n, len, 0);
    }

    //void inc_bignum(bignum n, Uint32 len)
    private static void inc_bignum(long[] n, int len, int offset) {
        //while ((++*n == 0) && (--len > 0))
        //    n++;

        //OUT_TAB_64("n", len, n);

        int i = 0;
        while ((++n[i] == 0) && (--len > 0))
            i++;

        //OUT_TAB_64("n", len, n);

    }

    private static void clear_tmp_vars(int len) {
        init_bignum(glob1, 0, len);
        init_bignum(glob2, 0, len);
        init_bignum(glob1_hi_inv, 0, 4);
        init_bignum(glob1_hi, 0, 4);
        glob1_bitlen = 0;
        glob1_hi_bitlen = 0;
        glob1_len_x2 = 0;
        glob1_hi_inv_lo = 0;
        glob1_hi_inv_hi = 0;
    }

    private static void init_pubkey() throws Exception {

        init_bignum(pubkey.key2, 0x10001, 64);

        int i = 0, i2 = 0;
        long tmp;
        byte[] keytmp = new byte[256];
        while (i < pubkey_str.length) {
            tmp = char2num[pubkey_str[i++]];
            tmp <<= 6;
            tmp |= char2num[pubkey_str[i++]];
            tmp <<= 6;
            tmp |= char2num[pubkey_str[i++]];
            tmp <<= 6;
            tmp |= char2num[pubkey_str[i++]];

            keytmp[i2++] = (byte) ((tmp >> 16) & 0xff);
            keytmp[i2++] = (byte) ((tmp >> 8) & 0xff);
            keytmp[i2++] = (byte) (tmp & 0xff);
        }

        key_to_bignum(pubkey.key1, keytmp, 64);
        pubkey.len = (int) (bitlen_bignum(pubkey.key1, 64) - 1);

		
		/*OUT_TAB("keytmp", 56, keytmp);
		OUT_TAB_64("pubkey.key1", 64, pubkey.key1);
		
	    System.out.println(">>>>>>>>>>>>");
	    System.out.println("pubkey.len = 0x" + String.format("%04X ", pubkey.len));
	    System.out.println(">>>>>>>>>>>>");*/
    }

    // Uint32 bitlen_bignum(bignum n, Uint32 len)
    private static long bitlen_bignum(long[] n, long len) {
        long ddlen, bitlen, mask;
        ddlen = len_bignum(n, len);
        if (ddlen == 0)
            return 0;
        bitlen = ddlen * 32;
        mask = 0x80000000;
        while ((mask & n[(int) (ddlen - 1)]) == 0) {
            mask >>= 1;
            bitlen--;
        }

        return bitlen;
    }

    // Uint32 len_bignum(bignum n, Uint32 len)
    private static long len_bignum(long[] n, long len) {
        long i;
        i = len - 1;
        while ((i >= 0) && (n[(int) i] == 0))
            i--;
        return i + 1;
    }

    /*
     * void key_to_bignum(bignum n, char *key, Uint32 len)
{
    Uint32 keylen;
    int i;

    if (key[0] != 2)
        return;
    key++;

    if (key[0] & 0x80) {
        keylen = 0;
        for (i = 0; i < (key[0] & 0x7f); i++)
            keylen = (keylen << 8) | key[i+1];
        key += (key[0] & 0x7f) + 1;
    } else {
        keylen = key[0];
        key++;
    }
    if (keylen <= len*4)
        move_key_to_big(n, key, keylen, len);

}
     */
    private static void key_to_bignum(long[] n, byte[] key, long len) throws Exception {
        long keylen;
        int i;
        int offset = 0;
        //System.out.println("key[0] = 0x" + String.format("%02X", key[offset]));

        if (key[0] != 2)
            return;
        offset++;
        //System.out.println("key[0] = 0x" + String.format("%02X", key[offset]));

        if ((key[1] & 0x80) != 0) {
	    	/*System.out.println("1 !");
	        keylen = 0;
	        for (i = 0; i < (key[0] & 0x7f); i++)
	            keylen = (keylen << 8) | key[i+1];
	        key[0] += (key[0] & 0x7f) + 1;*/
            throw new Exception("not implemented");
        } else {
            keylen = key[1];
            offset++;
        }
        //System.out.println("key[0] = 0x" + String.format("%02X", key[offset]));

        if (keylen <= len * 4)
            move_key_to_big(n, key, keylen, len, offset);

    }


    // void move_key_to_big(bignum n, char *key, Uint32 klen, Uint32 blen)
    private static void move_key_to_big(long[] n, byte[] key, long klen, long blen, int offset) {

        byte sign;
        int i = 0;
        byte[] buff = new byte[n.length * 4];

        if ((key[0] & 0x80) != 0)
            sign = (byte) 0xff;
        else
            sign = 0;

        // System.out.println("sign = " + sign);
        //System.out.println("klen = " + klen);
        //System.out.println("blen = " + blen);

        for (i = (int) (blen * 4); i > klen; i--) {
            //System.out.println("1) i = " + i);
            buff[i - 1] = sign;
        }
        for (; i > 0; i--) {
            //System.out.println("2) i = " + i);
            //System.out.println("2) key[klen-i] = " + String.format("%02X", key[(int) (klen-i+offset)]));
            buff[i - 1] = key[(int) (klen - i + offset)];
        }

        // Copy to bignum
        ByteBuffer bf = ByteBuffer.wrap(buff);
        bf.order(ByteOrder.LITTLE_ENDIAN);
        for (int j = 0; j < 64; j++)
            n[j] = bf.getInt();
    }

    /**
     * <p>
     * Initialize a {@code bignum}.
     * </p>
     * <p>
     * {@code bignum} is
     * <code>typedef Uint32 bignum[64];</code>
     * </p>
     *
     * @param n   {@code bignum} to initialize
     * @param val Initial value of bytes of {@code bignum}
     * @param len Length of the {@code bignum}
     */
    private static void init_bignum(long[] n, long val, long len) {
        //void init_bignum(bignum n, Uint32 val, Uint32 len)

        for (int i = 0; i < n.length; i++)
            n[i] = (long) 0;

        n[0] = val;
    }

    private static long len_predata() {
        long a = (pubkey.len - 1) / 8;
        return (55 / a + 1) * (a + 1);
    }
}
